注解和资源注入

Java 元数据(Metadata)规范(JSR-175),是J2SE 5.0和更高版本的一部分,提供一种在Java代码中指定配置数据的方法。Java代码中的元数据也被称为注解。在JavaEE中,注解用于声明对外部资源的依赖和在Java代码中的配置数据而无需在配置文件中定义该数据。

本节描述了在Java EE技术兼容的Servlet容器中注解和资源注入的行为。本节扩展了Java EE规范第5节标题为“资源,命名和注入”。

注解必须支持以下容器管理的类,其实现了以下接口并在web应用部署描述符中声明,或使用定义在8.1节“注解和可插拔性”的注解声明或编程式添加的。

TABLE 15-1 Components and Interfaces supporting Annotations and Dependency Injection

组件类型 实现下面接口的类
Servlets javax.servlet.Servlet
Filters javax.servlet.Filter
Listeners javax.servlet.ServletContextListener javax.servlet.ServletContextAttributeListener javax.servlet.ServletRequestListener javax.servlet.ServletRequestAttributeListener javax.servlet.http.HttpSessionListener javax.servlet.http.HttpSessionAttributeListener javax.servlet.http.HttpSessionIdListener javax.servlet.AsyncListener

Web 容器不需要为存在注解的除了上表15-1列出的那些类执行资源注入。

引用必须在任何生命周期方法调用之前注入,且组件实例对应用是可用的。

在一个web应用中,使用资源注入的类只有当它们位于WEB-INF/classes目录,或如果它们被打包到位于WEB-INF目录下的jar文件中,它们的注解将被处理。容器可以选择性地为在其他地方的应用类路径中找到的类处理资源注入注解。

Web 应用部署描述符的web-app元素上包含一个metadata-complete属性。metadata-complete属性定义了web.xml描述符是否是完整的,或是否应考虑部署过程中使用的其他资源的元数据。元数据可能来自web.xml文件、web-fragment.xml文件、WEB-INF/classes中的类文件上的注解、和WEB-INF/lib目录中的jar文件中的文件上的注解。如果metadata-complete设置为“true”,部署工具仅检查web.xml文件且必须忽略如出现在应用的类文件上的@WebServlet@WebFilter、和@WebListener注解,且必须也忽略WEB-INF/lib中的打包在jar文件的任何web-fragment.xml描述符。如果metadata-complete没有指定或设置为“false”,部署工具必须检查类文件和web-fragment.xml文件的元数据,就像前面指定的那样。

web-fragment.xml的web-fragment元素也包含了metadata-complete属性。该属性定义了对于给定片段的web-fragment.xml描述符是否是完整的,或者是否应该扫描相关的jar文件中的类中的注解。如果metadata-complete设置为“true”,部署工具仅检查web-fragment.xml文件且必须忽略如出现在fragment的类文件上的@WebServlet@WebFilter、和@WebListener注解。如果metadata-complete没有指定或设置为“false”,部署工具必须检查类文件的元数据。

以下是Java EE技术兼容的web容器需要的注解。

@DeclareRoles

该注解用于定义由应用安全模型组成的安全角色。该注解指定在类上,且它用于定义能从注解的类的方法内测试(即,通过调用 isUserInRole)的角色。由于用在 @RolesAllowed 而隐式声明的角色,不必使用@DeclareRoles 注解明确声明。@DeclareRoles 注解仅可以定义在实现了javax.servlet.Servlet 接口或它的一个子类的类中。

以下是如果使用该注解的一个例子。

CODE EXAMPLE 15-3 @DeclareRoles Annotation Example

  1. @DeclareRoles("BusinessAdmin")
  2. public class CalculatorServlet {
  3. //...
  4. }

声明 @DeclareRoles(“BusinessAdmin”) 等价于如下在 web.xml 中的定义。

CODE EXAMPLE 15-4 @DeclareRoles web.xml

  1. <web-app>
  2. <security-role>
  3. <role-name>BusinessAdmin</role-name>
  4. </security-role>
  5. </web-app>

该注解不是用于重新链接(relink)应用角色到其他角色。当这样的链接是必需的,它是通过在相关的部署描述符中定义一个适当的security-role-ref来实现。

当从注解的类调用isUserInRole时,与调用的类关联的调用者身份以相同名称作为isCallerInRole的参数来测试角色成员身份。如果已经定义了参数role-name的一个security-role-ref,调用者测试映射到role-name的角色成员身份。

为进一步了解@DeclareRoles注解细节,请参考Java™平台™的通用注解(Common Annotation)规范((JSR 250)第2.12节。

@EJB

企业级 JavaBean™ 3.2 (EJB) 组件可以从一个 web 组件使用 @EJB 注解引用。@EJB注 解提供了与部署描述符中声明 ejb-ref 或 ejb-local-ref 元素等价的功能。有一个相应的 @EJB 注解的字段被注入一个相应的EJB组件的引用。

一个例子:

  1. @EJB private ShoppingCart myCart;

在上述情况下,到 EJB 组件“myCart”的一个引用被注入作为私有字段“myCart”的值,在声明注入的类可用之前。

进一步了解@EJB注解的行为请参考EJB 3.2规范(JSR 345)第11.5.1节。

@EJBs

@EJBs 注解允许在单个资源上声明多于一个 @EJB 注解。

一个例子:

CODE EXAMPLE 15-5 @EJBs Annotation Example

  1. @EJBs({@EJB(Calculator), @EJB(ShoppingCart)})
  2. public class ShoppingCartServlet {
  3. //...
  4. }

上面例子中的 EJB 组件 ShoppingCart 和 Calculator 对ShoppingCartServlet 是可用的。ShoppingCartServlet 仍然必须使用 JNDI 查找引用,但不需要声明在 web.xml 文件中。

@Resource

@Resource 注解用于声明到资源的引用,如一个数据源(data source)、Java消息服务(JMS)目的地、或环境项(environment entry)。该注解等价于在部署描述符中声明resource-ref、 message-destination-ref、或env-ref、或resource-env-ref元素。
@Resource 注解指定在一个类、方法或字段上。容器负责注入到由@Resource 注解声明的资源的引用和映射到适当的JNDI资源。请参考 Java EE 规范第5章进一步了解细节。

以下是一个@Resource注解的例子:

CODE EXAMPLE 15-6 @Resource Example

  1. @Resource
  2. private javax.sql.DataSource catalogDS;
  3. public getProductsByCategory() {
  4. // get a connection and execute the query
  5. Connection conn = catalogDS.getConnection();
  6. ..
  7. }

在上面的示例代码中,servlet、filter或listener声明一个javax.sql.DataSource 类型的 catalogDS 字段,在该组件对应用可用之前由容器注入数据源引用。数据源JNDI映射是从字段名“catalogDS”和类型(javax.sql.DataSource)推导出来的。此外,catalogDS数据源不再需要定义在部署描述符中。

进一步了解@Resource注解的语义请参考Java™平台™的通用注解规范(JSR 250)第2.3节和Java EE 7规范第5.2.5节。

@PersistenceContext

该注解指定容器管理的用于引用持久化单元(persistence unit)的实体管理器(entity manager)。

一个例子:

CODE EXAMPLE 15-7 @PersistenceContext Example

  1. @PersistenceContext (type=EXTENDED)
  2. EntityManager em;

进一步了解@PersistenceContext注解的行为请参考Java持久化API(Java Persistence API)2.1版本第10.5.1节(JSR 338)。

@PersistenceContexts

PersistenceContexts 注解允许在一个资源上声明多于一个@PersistenceContext。进一步了解 @PersistenceContexts 注解的行为请参考Java持久化API(Java Persistence API)2.1版本第10.5.1节(JSR 338)。

@PersistenceUnit

@PersistenceUnit 注解提供声明在 servlet 中的到企业级 JavaBean组件实体管理器工厂(entity manager factory)的引用。实体管理器工厂绑定到一个单独的persistence.xml 配置文件,该文件在EJB 3.2规范(JSR 345)中 11.10 节中描述。

一个示例:

CODE EXAMPLE 15-8 @PersistenceUnit Example

  1. @PersistenceUnit
  2. EntityManagerFactory emf;

进一步了解@ PersistenceUnit注解的行为请参考Java持久化API(Java Persistence API)2.1版本第10.5.2节(JSR 338)。

@PersistenceUnits

该注解允许在一个资源上声明多于一个 @PersistentUnit。进一步了解@ PersistentUnits 注解的行为请参考Java持久化API(Java Persistence API)2.1版本第10.5.2节(JSR 338)。

@PostConstruct

@PostConstruct 注解声明在一个无参的方法上,且必须不抛出任何检查的异常。返回值必须是void。该方法必须在资源注入完成之后被调用且在组件的任何生命周期方法之前被调用。

示例

CODE EXAMPLE 15-9 @PostConstruct Example

  1. @PostConstruct
  2. public void postConstruct() {
  3. ...
  4. }

上面的示例展示了一个使用 @PostConstruct 注解的方法。
@PostConstruct 注解必须支持那些支持依赖注入的所有类并即使该类不要求任何资源注入也会被调用。如果该方法抛出未受查一次,该类必须不被放入服务中且该实例没有方法被调用。

参考Java EE规范第2.5节和Java™平台™的通用注解规范的第2.5节获取更多细节。

@PreDestroy

@PreDestroy 注解声明在容器管理组件的一个方法上。该方法在容器移除该组件之前调用。

一个例子:

CODE EXAMPLE 15-10 @PreDestroy Example

  1. @PreDestroy
  2. public void cleanup() {
  3. // clean up any open resources
  4. ...
  5. }

使用@PreDestroy注解的该方法必须返回void且必须不抛出受查异常。该方法可以是public、protected、package私有或private。该方法必须不是static的,但它可以是final的。

参考JSR 250第2.6节获取更多细节。

@Resources

由于Java元数据规范不允许在相同的注解目标以相同名字使用多个注解,因此@Resources注解充当容器的多个@Resource注解。

一个例子:

CODE EXAMPLE 15-11 @Resources Example

  1. @Resources ({
  2. @Resource(name=”myDB type=javax.sql.DataSource),
  3. @Resource(name=”myMQ type=javax.jms.ConnectionFactory)
  4. })
  5. public class CalculatorServlet {
  6. //...
  7. }

在上面的示例中 CalculatorServlet 通过 @Resources 注解 使的 JMS 连接工厂和数据源变得可用。

@Resources 注解的语义是进一步详细的常见的注释的Java™平台™规范(JSR 250)2.4节

@RunAs

@RunAs 注解等价于部署描述符中的run-as注解。@RunAs注解可能仅定义在javax.servlet.Servlet 接口或它的子类的类实现上。

一个例子:

CODE EXAMPLE 15-12 @RunAs Example

  1. @RunAs(“Admin”)
  2. public class CalculatorServlet {
  3. @EJB private ShoppingCart myCart;
  4. public void doGet(HttpServletRequest, req, HttpServletResponse res) {
  5. //....
  6. myCart.getTotal();
  7. //....
  8. }
  9. }
  10. //....
  11. }

@RunAs(“Admin”) 语句等价于:

CODE EXAMPLE 15-13 @RunAs web.xml Example

  1. <servlet>
  2. <servlet-name>CalculatorServlet</servlet-name>
  3. <run-as>Admin</run-as>
  4. </servlet>

以上示例展示了当调用 myCart.getTotal() 方法时 Servlet 如何使用@RunAs 注解来传播安全身份“Admin”到 EJB 组件。进一步了解传播身份的细节请看第15.3.1节“EJB™调用的安全身份传播”。

进一步了解@RunAs注解的细节请参考Java™平台™的通用注解规范(JSR 250)第2.7节。

@WebServiceRef

@WebServiceRef 注解在一个web组件中使用可能在部署描述符中的resource-ref 相同的方式提供一个到 web service 的引用。

一个例子:

  1. @WebServiceRef private MyService service;

在这个例子中,一个到web service“MyService”的引用将被注入到声明该注解的类。

@WebServiceRefs

这个注解允许在单个资源上声明多于一个的 @WebServiceRef 注解。进一步了解这个注解的行为请参考JAX-WS规范(JSR 224)第7章节。

JavaEE 要求的上下文和依赖注入

在一个支持JavaEE(CDI)上下文和依赖注入且CDI开启的产品中,实现必须支持使用CDI managed bean。Servlet、Filter、Listener和HttpUpgradeHandler必须支持CDI注入和描述在EE.5.24节的拦截器使用,JavaEE 7平台规范“支持依赖注入”。